/**
 * 
 */
package br.com.skynix.tcmc.base.managedbeans;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import org.apache.log4j.Logger;

import br.com.skynix.tcmc.base.model.EntidadeAuditavel;
import br.com.skynix.tcmc.base.spring.IServiceAuditavel;
import br.com.skynix.tcmc.model.rh.Funcionario;
import br.com.skynix.tcmc.model.seguranca.Usuario;
import br.com.skynix.tcmc.util.OperacaoEnum;
import br.com.skynix.tcmc.util.WebUtil;

/**
 * @author mgrb
 * 
 */
public abstract class AbstractCRUDManagedBean<T extends EntidadeAuditavel>
		extends AbstractManagedBean {

	private static final Logger LOGGER = Logger
			.getLogger(AbstractCRUDManagedBean.class);

	private T modeloPesquisa;

	private T modeloEdicao;

	private T[] modelosSelecionados;

	private AbstractDataProvider<T> dataProvider;

	private Class<T> classePersistente;

	/**
	 * 
	 */
	public AbstractCRUDManagedBean() {
		this.classePersistente = getClassePersistente();
		setOperacao(OperacaoEnum.PESQUISAR);
		resetCrud();
		dataProvider = new AbstractDataProvider<T>() {
			@Override
			protected IServiceAuditavel<T> getService() {
				return AbstractCRUDManagedBean.this.getService();
			}

			@Override
			protected T getModelo() {
				return AbstractCRUDManagedBean.this.getModeloPesquisa();
			}
		};
	}

	/**
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected Class<T> getClassePersistente() {
		return (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/**
	 * 
	 */
	private void resetCrud() {
		modeloPesquisa = null;
		modeloEdicao = null;
		modelosSelecionados = null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.skynix.tcmc.base.AbstractManagedBean#getDataProvider()
	 */
	public AbstractDataProvider<T> getDataProvider() {
		return dataProvider;
	}

	/**
	 * @return
	 */
	protected abstract IServiceAuditavel<T> getService();

	/**
	 * @return the selectedModels
	 */
	public T[] getModelosSelecionados() {
		return modelosSelecionados;
	}

	/**
	 * @param selectedModels the selectedModels to set
	 */
	public void setModelosSelecionados(T[] selectedModels) {
		this.modelosSelecionados = selectedModels;
	}

	/**
	 * @return the modeloPesquisa
	 */
	public T getModeloPesquisa() {
		return modeloPesquisa;
	}

	/**
	 * @param modeloPesquisa the modeloPesquisa to set
	 */
	public void setModeloPesquisa(T modeloPesquisa) {
		this.modeloPesquisa = modeloPesquisa;
	}

	/**
	 * @return the modeloEdicao
	 */
	public T getModeloEdicao() {
		return modeloEdicao;
	}

	/**
	 * @param modeloEdicao the modeloEdicao to set
	 */
	public void setModeloEdicao(T modeloEdicao) {
		this.modeloEdicao = modeloEdicao;
	}

	public void irParaInclusao() {
		setOperacao(OperacaoEnum.INCLUIR);
		modeloEdicao = criarInstanciaPadraoModelo();
	}

	/**
	 * @param modelo
	 * 
	 */
	private void inicializarEmpresa(Object modelo) {
		Funcionario func = getUsuarioLogado().getFuncionario();
		if (func != null) {
			try {
				Field field = classePersistente.getDeclaredField("empresa");
				field.setAccessible(true);
				field.set(modelo, func.getEmpresa());
			}
			catch (IllegalArgumentException exception) {
				LOGGER.error("Falha ao inicializar Empresa no CRUD", exception);
			}
			catch (SecurityException exception) {
				LOGGER.error("Falha ao inicializar Empresa no CRUD", exception);
			}
			catch (NoSuchFieldException exception) {
				LOGGER.error("Falha ao inicializar Empresa no CRUD", exception);
			}
			catch (IllegalAccessException exception) {
				LOGGER.error("Falha ao inicializar Empresa no CRUD", exception);
			}
		}
	}

	/**
	 * @return
	 */
	protected Usuario getUsuarioLogado() {
		return (Usuario) FacesContext.getCurrentInstance().getExternalContext()
				.getSessionMap().get(WebUtil.USER_UUID);
	}

	public void irParaPesquisa() {
		modelosSelecionados = null;
		modeloEdicao = null;
		setOperacao(OperacaoEnum.PESQUISAR);

		if (modeloPesquisa == null) {
			modeloPesquisa = criarInstanciaPadraoModelo();
		}
	}

	protected T criarInstanciaPadraoModelo() {
		T modelo = null;
		try {
			modelo = classePersistente.newInstance();
			inicializarEmpresa(modelo);
		}
		catch (InstantiationException exception) {
			LOGGER.error("Falha ao inicializar Empresa no CRUD", exception);
		}
		catch (IllegalAccessException exception) {
			LOGGER.error("Falha ao inicializar Empresa no CRUD", exception);
		}
		return modelo;
	}

	public String carregarCRUD() {
		resetCrud();
		irParaPesquisa();
		return getPathParaModuloCRUD();
	}

	protected abstract String getPathParaModuloCRUD();

	public void irParaAlteracao() {
		if (modelosSelecionados == null || modelosSelecionados.length != 1) {
			adcionarMensagem(FacesMessage.SEVERITY_ERROR,
					"crud.erro.selecaoAlteracao",
					"crud.erro.selecaoAlteracao.detalhe", false);
		}
		else {
			modeloEdicao = carregarModeloEdicao(modelosSelecionados[0]);
			setOperacao(OperacaoEnum.ALTERAR);
		}
	}

	/**
	 * @param t
	 * @return
	 */
	protected T carregarModeloEdicao(T t) {
		return t;
	}

	public void irParaVisualizacao() {
		if (modelosSelecionados == null || modelosSelecionados.length != 1) {
			adcionarMensagem(FacesMessage.SEVERITY_ERROR,
					"crud.erro.selecaoVisualizacao",
					"crud.erro.selecaoVisualizacao.detalhe", false);
		}
		else {
			modeloEdicao = carregarModeloEdicao(modelosSelecionados[0]);
			setOperacao(OperacaoEnum.VISUALIZAR);
		}
	}

	public void salvarAction() {
		if (isOperacaoAlterar()) {
			getService().alterar(modeloEdicao);
			adcionarMensagem("crud.info.alteracaoSucesso", false);
		}
		else if (isOperacaoIncluir()) {
			getService().inserir(modeloEdicao);
			adcionarMensagem("crud.info.inclusaoSucesso", false);
		}
		irParaPesquisa();
	}

	public void excluirModelosSelecionados(ActionEvent actionEvent) {
		if (modelosSelecionados == null || modelosSelecionados.length == 0) {
			adcionarMensagem(FacesMessage.SEVERITY_ERROR,
					"crud.erro.selecaoExclusao",
					"crud.erro.selecaoExclusao.detalhe", false);
		}
		else {
			for (T modeloExcluir : modelosSelecionados) {
				getService().excluir(modeloExcluir);
			}
			modelosSelecionados = null;
			adcionarMensagem("crud.info.exclusaoSucesso", false);
			setOperacao(OperacaoEnum.PESQUISAR);
		}
	}
}
